////////////////////////////////////////////////////////////////////////////////
// This file is part of the Mist compiler.                                    //
//                                                                            //
// Copyright (C) 2009, 2011 The Mist project                                  //
//                                                                            //
// For a list of contributors, see the COMMITTERS file.                       //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// in the COPYING file. If not, see <http://www.gnu.org/licenses/>.           //
////////////////////////////////////////////////////////////////////////////////

///
/// \file
/// Contains the implementation of the Expression::TypeOf methods from tree.hpp.
///

#include "tree.hpp"

////////////////////////////////////////////////////////////////////////////////
// Includes                                                                   //
////////////////////////////////////////////////////////////////////////////////

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

#include "exclusivevisitor.hpp"
#include "operator.hpp"
#include "parse.hpp"
#include "symbol.hpp"

////////////////////////////////////////////////////////////////////////////////
// FindTypeVisitor Class                                                      //
////////////////////////////////////////////////////////////////////////////////

/// Visits one Expression inherited AST node and finds its type.
///
class FindTypeVisitor : public ExclusiveVisitor {
	public:
		
		VISIT_FUNCTIONS_FROM (
			(FunctionCall)
			(OperatorCall)
			(Reference)
			(BooleanLiteral)
			(IntegerLiteral)
			(ChainExpression)
			(ForallExpression)
			(ThereisExpression)
		)
		
		sptr<Type> foundType;
};

////////////////////////////////////////////////////////////////////////////////
// Function Implementations                                                   //
////////////////////////////////////////////////////////////////////////////////

void Expression::TypeOf::set_expression(wptr<Expression> expression) {
	_expression = expression;
}

void Expression::TypeOf::compute_value() {
	sptr<FindTypeVisitor> visitor(new FindTypeVisitor);
	
	_expression->accept(visitor);
	
	if (visitor->foundType)
		set_value(visitor->foundType);
	else
		set_error();
}

////////////////////////////////////////////////////////////////////////////////

void FindTypeVisitor::visit(wptr<FunctionCall> node) {
	assert(node);
	
	if (typeid(*node->base->type()) == typeid(FunctionType))
		foundType = node->base->type().cast<FunctionType>()->returnType;
	// else, foundType remains null, and an error state will be set
}

void FindTypeVisitor::visit(wptr<OperatorCall> node) {
	assert(node);
	
	vector<sptr<Reference> > references = get_operator_references(node->op, weak_vector(node->operands), node->scope());
	
	assert(typeid(*references.at(0)->symbol()->type) == typeid(FunctionType));
	
	// The return-type of the outer single operator is the type of the
	// operator-call.
	//
	foundType = references.at(0)->symbol()->type.cast<FunctionType>()->returnType;
}

void FindTypeVisitor::visit(wptr<Reference> node) {
	assert(node);
	
	foundType = node->symbol()->type;
}

void FindTypeVisitor::visit(wptr<BooleanLiteral> node) {
	assert(node);
	
	foundType = parse<Type>(".bool", node->scope());
}

void FindTypeVisitor::visit(wptr<IntegerLiteral> node) {
	assert(node);
	
	foundType = parse<Type>(".int", node->scope());
}

void FindTypeVisitor::visit(wptr<ChainExpression> node) {
	assert(node);
	
	foundType = parse<Type>(".bool", node->scope());
}

void FindTypeVisitor::visit(wptr<ForallExpression> node) {
	assert(node);
	
	foundType = parse<Type>(".bool", node->scope());
}

void FindTypeVisitor::visit(wptr<ThereisExpression> node) {
	assert(node);
	
	foundType = parse<Type>(".bool", node->scope());
}

